/*  
 * Copyright 2007 MBARI 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.mbari.timeline.piccolo;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.mbari.timeline.model.MultipartEvent;

import com.domainlanguage.time.Duration;
import com.domainlanguage.time.TimeInterval;
import com.domainlanguage.time.TimePoint;

import edu.umd.cs.piccolo.event.PDragEventHandler;
import edu.umd.cs.piccolo.event.PInputEvent;
import edu.umd.cs.piccolo.util.PDimension;
import edu.umd.cs.piccolox.nodes.PLine;
import edu.umd.cs.piccolox.util.LineShape;
import edu.umd.cs.piccolox.util.XYArray;

/**
 * This event node is meant to represent a MultipartEvent with two durations.
 * The start of both durations and the end of the second duration will all appear
 * as colored lines (left edge, middle edge, right edge). Each of these lines
 * is draggable and dragging them across the timeline will change the MultiPartEvent's
 * time. The display properties of the lines can be adjusted by using the get*Edge() 
 * methods and setting the stroke and strokePaint on the lines.
 * 
 * @author Andrew Chase
 *
 */
@SuppressWarnings("serial")
public class TwoPartEventNode extends EventNode{
	private final MultipartEvent event;
    private final PLine leftEdge;
    private final PLine middleEdge;
    private final PLine rightEdge;
    private final Timeline timeline;
    private TimePoint lastTimePoint;
 
    
    public TwoPartEventNode(MultipartEvent event, Timeline timeline){
    	super(event, timeline);
    	this.event = event;
    	lastTimePoint = event.getTime();
        this.timeline = timeline;
        XYArray lineArray = new XYArray(2);
        lineArray.addPoint(0, 0, 0);
        lineArray.addPoint(1, 0, HEIGHT);
        leftEdge = new PLine(new LineShape(lineArray));
        float[] dash = {3, 2};
        BasicStroke dashed = new BasicStroke(1.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0f, 
            dash, 0);
        leftEdge.setStroke(dashed);
        leftEdge.setStrokePaint(Color.green);
        rightEdge = new PLine(new LineShape(lineArray));
        rightEdge.setStrokePaint(Color.red);
        rightEdge.setStroke(dashed);
        middleEdge = new PLine(new LineShape(lineArray));
        middleEdge.setStrokePaint(Color.yellow);
        middleEdge.setStroke(new BasicStroke(3.0f));
        this.addChild(leftEdge);
        this.addChild(middleEdge);
        this.addChild(rightEdge);
        addDragListeners();
        addModelListener();
        this.setPaint(new Color(0f, 0f, 0f, 0f)); //transparent
        setLineCoords();
        syncXCoordToTime();
    }
    
    private void addModelListener() {
		event.addPropertyChangeListener("time", new PropertyChangeListener(){
			public void propertyChange(PropertyChangeEvent evt) {
				if(!event.getTime().equals(lastTimePoint)){
					return;
				}
				syncXCoordToTime();
			}
		});
	}

	private void addDragListeners(){
    	this.addInputEventListener(new PDragEventHandler(){
    		protected void startDrag(PInputEvent e){
                super.startDrag(e);
                e.setHandled(true);
                e.getPickedNode().moveToFront();
            }
            public void drag(PInputEvent inputEvent){
            	Point2D nodePosition = inputEvent.getPositionRelativeTo(TwoPartEventNode.this);
                Point2D timelinePosition = inputEvent.getPositionRelativeTo(timeline);
                PDimension delta = inputEvent.getDeltaRelativeTo(TwoPartEventNode.this);
                /*
                 * subtract the node position x coordinate from the timeline position x coordinate because
                 * we want to set the date that is represented by the left edge of this node and the user
                 * may have clicked on the middle or right edge. In those cases nodePosition.getX() will
                 * need to be subtracted away.
                 */
                double newDateXCoord = timelinePosition.getX() - nodePosition.getX();
                TimePoint newTime = timeline.xCoordinateToDate(newDateXCoord);
                if(!timeline.getTimeRange().includes(newTime)){
                    TimeInterval range = timeline.getTimeRange();
                    TimePoint eventTime = event.getTime();
                    //make sure we haven't wandered off the edge, if that happens the event will get
                    //stuck wherever it is. Reset it to the nearest edge.
                    if(range.includes(eventTime)){
                        if(eventTime.isAfter(range)){
                            event.setTime(range.end().minus(Duration.minutes(1)));
                        }else{
                            event.setTime(range.start().plus(Duration.minutes(1)));
                        }
                        TwoPartEventNode.this.syncXCoordToTime();
                    }
                	return;
                }
                event.setTime(newTime);
                TwoPartEventNode.this.offset(delta.getWidth(), 0);
            }
    	});
    }

    public void setLineCoords(){
    	TimePoint event2Start = event.getTime().plus(event.getDurationPart(0));
    	double distanceAway = //
    		timeline.dateToXCoordinate(event2Start) - //
    		timeline.dateToXCoordinate(event.getTime());
    	middleEdge.offset(distanceAway, 0);
    	TimePoint event2End = event2Start.plus(event.getDurationPart(1));
    	distanceAway = //
    		distanceAway + //
    		timeline.dateToXCoordinate(event2End) - //
    		timeline.dateToXCoordinate(event2Start);
    	rightEdge.offset(distanceAway, 0);
    	this.setBounds(this.getX(), this.getY(), distanceAway + 1 , HEIGHT);
    }

	public MultipartEvent getEvent() {
		return event;
	}

	public PLine getLeftEdge() {
		return leftEdge;
	}

	public PLine getMiddleEdge() {
		return middleEdge;
	}

	public PLine getRightEdge() {
		return rightEdge;
	}
}
